/*
 *作用：获取网址上的参数值
 *参数：getQueryVariable（‘参数名称’）
 *例：http://www.runoob.com/index.php?id=1&image=awesome.jpg
       getQueryVariable（‘id’）     
       结果1
 */
export function getQueryVariable(key: String, href: String): String {
  // 小程序没有window
  href = href || window.location.href
  let match = href.match(new RegExp('[?&]' + key + '=([^&]*)'))
  return (match && match[1] && decodeURIComponent(match[1])) || ''
}

/*
 *时间筛选类
 *传入参数0：昨天
 *传入参数1：近7天
 *传入参数0：本月
 */
//  -----------------------------------------------------------------------------------------------------------------
interface timeChange {
  startTime: string
  endTime: string
}

export class TimeChange {
  // 创建传入的对象信息
  readonly timeChange: timeChange = { startTime: '', endTime: '' }
  // 获取当前的时间
  private nowDate: Date = new Date()

  // 构造函数
  // ==========================================================================================
  constructor(num: number) {
    //昨日
    if (num == 0) {
      let afDay = this.afterDay()
      this.timeChange.startTime = afDay + '-00:00:00'
      this.timeChange.endTime = afDay + '-23:59:59'

      // 近7天
    } else if (num == 1) {
      let afDay = this.afterDay()
      let weekDay = this.weekDay()
      this.timeChange.startTime = weekDay + '-00:00:00'
      this.timeChange.endTime = afDay + '-23:59:59'

      // 本月
    } else if (num == 2) {
      let now = this.newDay()
      let mouthDay = this.mouthDay()
      this.timeChange.startTime = mouthDay + '-00:00:00'
      this.timeChange.endTime = now + '-23:59:59'
    }
  }

  // 配套方法
  // ==========================================================================================
  // 今天
  private newDay(): string {
    this.nowDate.setTime(this.nowDate.getTime())
    let s2 =
      this.nowDate.getFullYear() +
      '-' +
      (this.nowDate.getMonth() + 1) +
      '-' +
      this.nowDate.getDate()
    return s2
  }

  // 昨天
  // ==========================================================================================
  private afterDay(): string {
    // 昨天时间计算
    this.nowDate.setTime(this.nowDate.getTime() - 24 * 60 * 60 * 1000)
    let s1 =
      this.nowDate.getFullYear() +
      '-' +
      (this.nowDate.getMonth() + 1) +
      '-' +
      this.nowDate.getDate()
    return s1
  }

  // 近一周
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  private weekDay(): string {
    let nowTimer = new Date()
    // 一周时间计算
    let oneWeekDate = new Date(
      (nowTimer as unknown as number) - 7 * 24 * 3600 * 1000
    )
    let y = oneWeekDate.getFullYear()
    let m = oneWeekDate.getMonth() + 1
    let d = oneWeekDate.getDate()
    let formAtwDate = y + '-' + m + '-' + d

    return formAtwDate
  }

  // 本月一号
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  private mouthDay(): string {
    // 本月初时间计算
    let y = this.nowDate.getFullYear()
    let m = this.nowDate.getMonth() + 1
    let d = 1
    let forMatNowDate = y + '-' + m + '-' + d

    return forMatNowDate
  }
}

/*
 *判断当前页面是否隐藏或重新展示函数
 *参数：
  fun：一个对象，里面有两个函数成员；hiddenFun 用户离开了当前页面，即页面不可见时触发的函数；visibleFun 用户打开或回到页面，即页面可见触发的函数

 **使用**：
 按照个人需要使用传入对应的函数即可
 */
//  -----------------------------------------------------------------------------------------------------------------
export const pageState = (fun: any) => {
  // 对于参数结构，方便下方使用，并赋予默认值
  const { hiddenFun = () => {}, visibleFun = () => {} } = fun

  document.addEventListener('visibilitychange', function () {
    // 用户离开了当前页面，即页面不可见
    if (document.visibilityState === 'hidden') {
      hiddenFun()
    }
    // 用户打开或回到页面，即页面可见
    if (document.visibilityState === 'visible') {
      visibleFun()
    }
  })
}

/*
 *日期格式化函数
 *传入一个对象参数，该对象参数中包括了三个参数
 *date:支持时间戳（毫秒）和new Date()类的两个格式传参，默认 new Date()
 *format:日期格式的类型字符串，默认 'YYYY-MM-DD hh:mm:ss'
 *rules:验证规则对象，里面支持对于每个时间段的依次自定义字段content+完整展示开关abbreviation(完整展示true，省略展示false，默认true)
 *rules规则中对week（星期）完整展示开关abbreviation规则为 true汉字展示，false数值展示，且星期日，汉字统一为日，数值统一为7

 **整体传参结构参考**
 dateMsg:{
  date : new Date(),
  format : 'YY-MM-DD-星期WW hh:mm:ss',
  rules : {
    year : {content:'YY',abbreviation:true},
    month : {content:'MM',abbreviation:true},
    day : {content:'DD',abbreviation:true},
    week : {content:'WW',abbreviation:true},
    hour : {content:'hh',abbreviation:true},
    minute : {content:'mm',abbreviation:true},
    second : {content:'ss',abbreviation:true},
  },
 }

 **使用介绍**
 1、date参数接收数值number和object两个类型的传参，即时间戳和new Date()类
 2、date传参也是支持new Date(967741381000)这样格式的参数
 3、format传参必须是一个字符串形式，且需要替换的格式必需要是rules参数对象中定义好的字段进行替换，默认 年份以 YY ；月份以 MM ；日期以 DD ；小时以 hh ；分钟以 mm ；秒钟以 ss 。进行搭配
 4、format传参不必必须将 YY-MM-DD-星期WW hh:mm:ss 都满足，也可以只是 YY MM DD，甚至可以变为 '今天MM月DD日周WW的hh时，在YY年'
 5、rules规则参数中对每个时间段都可以在content参数里面进行自定义替换占位字符串，但需要保证替换后的字符串在 format 有体现，如果定义了 content 参数请不要为空
 6、rules规则参数中可以自定义某个时间段的abbreviation参数进行判断是否开启完整展示模式，完整展示即，年份展示4位，而不是2位，其他时间段遇到个位自动补全前面的0
 7、此函数支持直接调用，直接调用展示默认的样式，即当前时间的 'YY-MM-DD-星期WW hh:mm:ss' 格式
 8、本函数已经进行了默认参数赋值，故可以直接对需要的地方进行调整就行传参即可，可以不用完整传入，如，只需要改月份为简写模式，只需要传入对象{rules:{month:{abbreviation:false}}}
 */
// -----------------------------------------------------------------------------------------------------------------
// 本函数接口
interface dateMsgInterface {
  date?: number | Date
  format?: string
  rules?: dateMsgRules
}

// rules规则type
type dateMsgRules = { [k in dateMsgRulesList]?: dateMsgRulesItem }
// 配合rules规则的数据
type dateMsgRulesList =
  | 'year'
  | 'month'
  | 'day'
  | 'hour'
  | 'week'
  | 'minute'
  | 'second'
type dateMsgRulesItem = { content?: string; abbreviation?: boolean }

export const dateFormat: (dateMsg?: dateMsgInterface) => string | void = (
  dateMsg: dateMsgInterface = {}
) => {
  // 数据初步处理
  // ===========================================================================================================
  // 结构参数并做初始化赋值
  const {
    date = new Date(),
    format = 'YY-MM-DD-星期WW hh:mm:ss',
    rules = {},
  } = dateMsg

  // 对规则对象中数据进行结构并做初始化赋值，方便下方使用
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const {
    year = {},
    month = {},
    day = {},
    week = {},
    hour = {},
    minute = {},
    second = {},
  } = rules

  // 给年份对象结构并赋予初始值
  const { content: yearContent = 'YY', abbreviation: yearAbbreviation = true } =
    year

  // 给月份对象结构并赋予初始值
  const {
    content: monthContent = 'MM',
    abbreviation: monthAbbreviation = true,
  } = month

  // 给日期对象结构并赋予初始值
  const { content: dayContent = 'DD', abbreviation: dayAbbreviation = true } =
    day

  // 给星期对象结构并赋予初始值
  const { content: weekContent = 'WW', abbreviation: weekAbbreviation = true } =
    week

  // 给小时对象结构并赋予初始值
  const { content: hourContent = 'hh', abbreviation: hourAbbreviation = true } =
    hour

  // 给分钟对象结构并赋予初始值
  const {
    content: minuteContent = 'mm',
    abbreviation: minuteAbbreviation = true,
  } = minute

  // 给秒钟对象结构并赋予初始值
  const {
    content: secondContent = 'ss',
    abbreviation: secondAbbreviation = true,
  } = second

  // 需要改变的字符串的数据拷贝，防止对源数据产生修改
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  let formatTime = format

  // 判断数据变量声明
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  let ifYear = formatTime.includes(yearContent) // 判断是否有年份
  let ifMonth = formatTime.includes(monthContent) // 判断是否有月份
  let ifDay = formatTime.includes(dayContent) // 判断是否有日期
  let ifWeek = formatTime.includes(weekContent) // 判断是否有星期
  let ifHour = formatTime.includes(hourContent) // 判断是否有小时
  let ifMinute = formatTime.includes(minuteContent) // 判断是否有分钟
  let ifSecond = formatTime.includes(secondContent) // 判断是否有秒钟

  // 验证规则
  // ===========================================================================================================
  if (
    !ifYear &&
    !ifMonth &&
    !ifDay &&
    !ifWeek &&
    !ifHour &&
    !ifMinute &&
    !ifSecond
  ) {
    // 如果所有地方都不符合格式规则，则报错提醒
    return console.error(
      `目前暂无格式化时间的匹配项，请参考 rules 规则中：年份以 ${yearContent}；月份以 ${monthContent} ；日期以 ${dayContent} ；星期以 ${weekContent} ；小时以 ${hourContent} ；分钟以 ${minuteContent} ；秒钟以 ${secondContent} 。进行搭配`
    )

    // 对rules规则参数中的每个成员的 content 字段进行验证处理
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  } else if (
    !yearContent ||
    !monthContent ||
    !dayContent ||
    !weekContent ||
    !hourContent ||
    !minuteContent ||
    !secondContent
  ) {
    return console.error(
      `rules规则参数中${
        !yearContent
          ? 'year年份'
          : !monthContent
          ? 'month月份'
          : !dayContent
          ? 'day日期'
          : !weekContent
          ? 'week星期'
          : !hourContent
          ? 'hour小时'
          : !minuteContent
          ? 'minute分钟'
          : 'second秒钟'
      }规则的 content 字段不为空`
    )
  }

  // 逻辑操作区域
  // ===========================================================================================================
  // 创建time变量
  let time: Date

  // 判断传入的类型是否是数值，是的话进行创建 date的实例，否则直接给time赋值
  typeof date === 'number' ? (time = new Date(date)) : (time = date)

  // 判断是否有年份需要替换，有在进行替换，否则不用进行
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  if (ifYear) {
    // 根据年份规则中完整展示开关 abbreviation 进行判断是否进行完整展示（即年份四位展示，非完整两位展示）
    let newYear = yearAbbreviation
      ? time.getFullYear()
      : String(time.getFullYear()).substring(2)

    // 替换掉对应的年份内容
    formatTime = formatTime.replace(yearContent, String(newYear))
  }

  // 判断是否有月份需要替换，有在进行替换，否则不用进行
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  if (ifMonth) {
    // 根据月份规则中完整展示开关 abbreviation 进行判断是否进行完整展示（即未大于10的前面拼接0，非完整不用拼接）
    let newMonth = monthAbbreviation
      ? time.getMonth() + 1 < 10
        ? `0${time.getMonth() + 1}`
        : time.getMonth() + 1
      : time.getMonth() + 1

    // 替换掉对应的月份内容
    formatTime = formatTime.replace(monthContent, String(newMonth))
  }

  // 判断是否有日期需要替换，有在进行替换，否则不用进行
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  if (ifDay) {
    // 根据日期规则中完整展示开关 abbreviation 进行判断是否进行完整展示（即未大于10的前面拼接0，非完整不用拼接）
    let newDay = dayAbbreviation
      ? time.getDate() < 10
        ? `0${time.getDate()}`
        : time.getDate()
      : time.getDate()

    // 替换掉对应的日期内容
    formatTime = formatTime.replace(dayContent, String(newDay))
  }

  // 判断是否有星期需要替换，有在进行替换，否则不用进行
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  if (ifWeek) {
    // 星期汉字匹配数组
    let weekChineseList = ['日', '一', '二', '三', '四', '五', '六']

    /**
     * 星期替换规则中，abbreviation完整展示为汉字（如一），如果没有开启则是数值（如1）
     * 周末汉字表达，统一为日，数值返回为7
     */
    let weekDay = weekAbbreviation
      ? weekChineseList[time.getDay()]
      : time.getDay() === 0
      ? 7
      : time.getDay()

    // 替换掉对应的星期内容
    formatTime = formatTime.replace(weekContent, String(weekDay))
  }

  // 判断是否有小时需要替换，有在进行替换，否则不用进行
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  if (ifHour) {
    // 根据小时规则中完整展示开关 abbreviation 进行判断是否进行完整展示（即未大于10的前面拼接0，非完整不用拼接）
    let newHour = hourAbbreviation
      ? time.getHours() < 10
        ? `0${time.getHours()}`
        : time.getHours()
      : time.getHours()

    // 替换掉对应的小时内容
    formatTime = formatTime.replace(hourContent, String(newHour))
  }

  // 判断是否有分钟需要替换，有在进行替换，否则不用进行
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  if (ifMinute) {
    // 根据分钟规则中完整展示开关 abbreviation 进行判断是否进行完整展示（即未大于10的前面拼接0，非完整不用拼接）
    let newMinute = minuteAbbreviation
      ? time.getMinutes() < 10
        ? `0${time.getMinutes()}`
        : time.getMinutes()
      : time.getMinutes()

    // 替换掉对应的分钟内容
    formatTime = formatTime.replace(minuteContent, String(newMinute))
  }

  // 判断是否有秒钟需要替换，有在进行替换，否则不用进行
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  if (ifSecond) {
    // 根据秒钟规则中完整展示开关 abbreviation 进行判断是否进行完整展示（即未大于10的前面拼接0，非完整不用拼接）
    let newSecond = secondAbbreviation
      ? time.getSeconds() < 10
        ? `0${time.getSeconds()}`
        : time.getSeconds()
      : time.getSeconds()

    // 替换掉对应的分钟内容
    formatTime = formatTime.replace(secondContent, String(newSecond))
  }

  // 弹出已经格式化后的时间
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  return formatTime
}

/**
 *
 * @获取某天的起止时间戳
 * @num数值型参数默认0
 * 今日传入0，昨日传入-1，明日传入1以此类推
 * @弹出计算的时间戳对象
 */
// ---------------------------------------------------------------------------------------------
// 所需接口定义
interface getStartEndTimeReturn {
  startTime: number
  endTime: number
}
type getStartEndTimeFun = (num: number) => getStartEndTimeReturn

export const getStartEndTime: getStartEndTimeFun = (num: number = 0) => {
  // 一天的毫秒数
  const MillisecondsADay = 24 * 60 * 60 * 1000 * num
  // 今日开始时间戳
  const todayStartTime = new Date(new Date().setHours(0, 0, 0, 0)).getTime()
  // 今日结束时间戳
  const todayEndTime = new Date(new Date().setHours(23, 59, 59, 999)).getTime()
  // 需要的开始时间戳
  const needStartTime = todayStartTime + MillisecondsADay
  // 需要的结束时间戳
  const needEndTime = todayEndTime + MillisecondsADay

  // 弹出一个对象包含需要的开始时间戳 startTime 和结束时间戳 endTime
  return { startTime: needStartTime, endTime: needEndTime }
}

/**
 * @百分比计算函数
 * @nowNumber_当前的值
 * @MaxNumber_最大值
 * @singleDigit_是否是以个位小数形式返回_不是的话则是百分比两到三位数形式返回_默认是小数true
 */
// ---------------------------------------------------------------------------------------------
// 所需接口定义
type calculatePercentageFun = (
  nowNumber: number,
  MaxNumber: number,
  singleDigit?: boolean
) => number

export const calculatePercentage: calculatePercentageFun = (
  nowNumber: number,
  MaxNumber: number,
  singleDigit: boolean = true
) => {
  // 百分比计算公式，保留两位小数，并超过时默认为1
  let needNumber =
    nowNumber < MaxNumber ? Number(Number(nowNumber / MaxNumber).toFixed(2)) : 1

  // 根据返回形式状态 singleDigit 进行返回对应的值
  return singleDigit ? needNumber : needNumber * 100
}

/**
 * @动态高度计算函数
 * @idList需要减去高度的id合计数组或一个字符串
 * @reference整个最高参考的高度id_没有传参则是默认app本页面的高度
 */
// ---------------------------------------------------------------------------------------------
// 所需接口定义
interface dynamicHeightReturn {
  height: number
  calculationHeight: number
}
type dynamicHeightFun = (
  idList?: [string] | string | [],
  reference?: string
) => dynamicHeightReturn

export const dynamicHeight: dynamicHeightFun = (
  idList: [string] | string | [] = [],
  reference: string = 'app'
) => {
  let needHeight = 0 // 要减去高度的承接值
  let idListType = typeof idList

  // 判断数组中是否有成员存在
  if (idList.length && idListType !== 'string') {
    // 因为存在类型转换，故在此做中间值取防止ts报错
    let list = idList as unknown as Array<string>

    // 是的话则对其进行循环，并获取到对应的高度值赋予 needHeight
    list.forEach((value) => {
      let dom = document.getElementById(value) // 获取这些的dom元素
      let domOffHeight = dom.offsetHeight // 获取dom除了外边距的高度
      // 获取dom的上外边距+下外边距的高度
      let domMarginHeight =
        Number(
          window.getComputedStyle(dom, null).marginBottom.replace('px', '')
        ) +
        Number(window.getComputedStyle(dom, null).marginTop.replace('px', ''))
      // 进行相加得到真实的高度
      let needNumber = domOffHeight + domMarginHeight
      // 然后赋值给需要减去的高度值
      needHeight += needNumber
    })
    // 判断传入的是否是一个有值的字符串，是的话进行直接id赋值操作
  } else if (idList && idListType === 'string') {
    needHeight = document.getElementById(
      idList as unknown as string
    ).offsetHeight
  }

  // 弹出最终计算出的高度值 和 之前减去成员总共值 的合集对象
  return {
    height: document.getElementById(reference).offsetHeight - needHeight,
    calculationHeight: needHeight,
  }
}

/**
 * @两个时间戳计算化为天数加小时函数
 * @starTime开始的时间戳
 * @endTime结束的时间戳
 * @弹出处理好的天数与小时的对象
 * 注：此函数为不足一小时依然按照一小时计算，故是向上取整
 */
// ---------------------------------------------------------------------------------------------
// 函数弹出类型接口
interface timestampChangeDaysHoursReturn {
  day: number
  hour: number
}

// 此函数ts定义
type timestampChangeDaysHoursTs = (
  starTime: number,
  endTime: number
) => timestampChangeDaysHoursReturn

export const timestampChangeDaysHours: timestampChangeDaysHoursTs = (
  starTime: number,
  endTime: number
) => {
  // 一天的毫秒数
  const MillisecondsADay = 24 * 60 * 60 * 1000
  // 一小时的毫秒数
  const MillisecondsAHour = 60 * 60 * 1000
  // 时间戳的差值
  const timeDifference = endTime - starTime

  // 距离的天数
  let differenceDay = Math.trunc(timeDifference / MillisecondsADay)

  // 距离的小时数
  let differenceHour = Math.ceil(
    (timeDifference - differenceDay * MillisecondsADay) / MillisecondsAHour
  )

  // 弹出计算好的时间对象（不足一小时依然按照一小时计算，故是向上取整）
  return {
    day: differenceDay,
    hour: differenceHour,
  }
}

/**
 * @最简单的性能测试函数
 * @fn需要测试的函数体
 * @param需要带入的参数_没有就不填
 * @name此测试的打印名称_可不填
 * 如果是多参数的则将所有参数作为数组成员写入,然后传入此数组即可
 * 如果只有一个参数，且还是个数组，则需要在在此数据外套一层数组包裹即可，如[param:array]
 */
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
interface Text<T, C> {
  (fn?: T, param?: C, name?: string): void
}
export const test: Text<Function, unknown> = (fn, param, name = '') => {
  // 开始性能计时
  console.time(name)

  // 执行待测试的方法
  param instanceof Array ? fn(...param) : fn(param)

  // 结束性能计时
  console.timeEnd(name)
}

/**
 * @数据深克隆方法_只针对数组或对象
 * @data传入需要深克隆的数据
 */
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
interface DeepClone {
  <T>(data: T, json?: boolean): T
}

export const deepClone: DeepClone = <T>(data: T, json = false) => {
  // 如果没有参数时，直接弹出并提示错误信息
  if (!data) {
    console.error('未存在深克隆参数')

    return

    // 判断是否开启了json深克隆模式的开关
  } else if (json) {
    // 是的话，直接进行弹出json好的属性
    return JSON.parse(JSON.stringify(data))
  }

  // 类型声明对象
  let dataStateArray = data instanceof Array
  let dataStateObject = data instanceof Object

  // 如果类型判断出不是数组或对象类型，则进行警告提示，并弹出此数据
  if (!dataStateArray && !dataStateObject) {
    console.warn('此参数不是对象或数组')
    return data
  }

  // 根据 data的数据类型，进行创建对应的深克隆原始类型
  let cloneData = dataStateArray ? [] : {}

  // 判断data是否是数组
  if (data instanceof Array) {
    // 是数组的话，进行循环遍历
    data.forEach((value, index) => {
      // 深克隆成员承接值
      let needValue: typeof value

      // 判断此成员是否是对象或者数组 是的话再次调用本方法
      value instanceof Object || value instanceof Array
        ? (needValue = deepClone(value))
        : (needValue = value)

      // 给对应的位置上进行赋值
      cloneData[index] = needValue
    })

    // 如果data是个对象
  } else if (data instanceof Object) {
    // 对对象进行遍历
    for (const key in data) {
      // 当前对应的data中的值
      let dataValue = data[key]
      // 后续数据承接值
      let needKeyValue: typeof dataValue
      // 判断当前在data中的值是什么类型，如果对象或数组的话，直接调用此方法，否则直接赋值
      dataValue instanceof Object || dataValue instanceof Array
        ? (needKeyValue = deepClone(dataValue))
        : (needKeyValue = dataValue)

      // 在深克隆数据承接对象中赋予对应的参数
      cloneData[key as unknown as string] = needKeyValue
    }
  }

  // 弹出最后的值
  return cloneData
}

/**
 * @判断用户当前设备是否是PC
 * @返回结果为一个布尔值
 * true为是pc，false则不是pc
 */
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
export const IsPC = () => {
  //  获取用户当前的设备信息
  let userAgentInfo = navigator.userAgent

  // 创建移动端内容的数组
  let Agents = [
    'Android',
    'iPhone',
    'SymbianOS',
    'Windows Phone',
    'iPad',
    'iPod',
  ]

  // 对 Agents 使用some方法，判断是否有成员和 当前用户设备信息一样,并赋值给 flagPc
  // （注意some方法返回是有成员一样为true，没有为false，所以要在此处取反）
  let flagPc = !Agents.some((value) => {
    return userAgentInfo.includes(value)
  })

  // 弹出结果
  return flagPc
}

/**
 * @计时器类
 * @callback参数为每次执行后回调的方法_会携带返回值
 * @返回结果为一个对象
 * day：天数；hour：小时；minute：分钟；second：秒钟
 * @打点调用方法
 * open:开启循环计时函数
 * end:停止循环计时,返回此时停止的时间对象
 * again:重置循环计时器,返回此时停止的时间对象
 */
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// 计时器类的 时间内容承接对象
type EstablishTimeTimeObj = {
  [key in EstablishTimeTimeObjContent]: number
}
// 时间内容承接对象属性
type EstablishTimeTimeObjContent = 'day' | 'hour' | 'minute' | 'second' | 'time'

export const EstablishTimer = class {
  protected timer: NodeJS.Timer // 定时器承接值
  protected timeObj: EstablishTimeTimeObj // 时间内容承接对象
  protected nowTime: number // 计时数值
  protected oneDaySeconds: number // 一天的总秒数
  protected oneHourSeconds: number // 一小时的总秒数
  protected oneMinuteSeconds: number // 一分钟的总秒数
  protected userCallBack: Function // 定时器中的回调函数

  // 构造函数
  // ------------------------------------------------------------------------------
  constructor(callback?: Function) {
    // 时间内容承接对象进行初始化话
    this.timeObj = {
      day: 0,
      hour: 0,
      minute: 0,
      second: 0,
      time: 0,
    }

    // 定时器中的回调函数初始化
    this.userCallBack = callback

    this.nowTime = 0 // 计时数值初始化
    this.oneDaySeconds = 60 * 60 * 24 // 一天的总秒数初始化
    this.oneHourSeconds = 60 * 60 // 一小时的总秒数初始化
    this.oneMinuteSeconds = 60 // 一分钟的总秒数初始化
  }

  /**
   * @开启循环计时函数
   * @定时器值赋予timer
   */
  // ------------------------------------------------------------------------------
  open: () => void = () => {
    // 防止多次open调用，故在每次open时清理一次定时器
    clearInterval(this.timer)
    // 在赋予定时器的值
    this.timer = setInterval(this.calculationTime, 1000)
  }

  /**
   * @停止循环计时
   * @返回此时停止的时间对象
   */
  // ------------------------------------------------------------------------------
  end: () => EstablishTimeTimeObj = () => {
    // 清理定时器
    clearInterval(this.timer)

    // 弹出当前的时间对象
    return this.timeObj
  }

  /**
   * @重置循环计时器
   * @返回此时重置后的时间对象
   */
  // ------------------------------------------------------------------------------
  again: () => EstablishTimeTimeObj = () => {
    clearInterval(this.timer)
    // 重置 计时数值
    this.nowTime = 0
    // 重置 时间内容承接对象
    this.timeObj = {
      day: 0,
      hour: 0,
      minute: 0,
      second: 0,
      time: 0,
    }

    // 弹出重置后的时间处理对象
    return this.timeObj
  }

  /**
   * @定时器内部使用函数
   */
  // ------------------------------------------------------------------------------
  protected calculationTime: () => void = () => {
    this.nowTime += 1 // 计时数值自增1

    // 计算此时的天数
    // ================================================================
    let needDay = this.nowTime / this.oneDaySeconds //得到天数的值
    let truncDay = Math.trunc(needDay) // 天数取整

    // 计算此时的小时
    // ================================================================
    let needHour =
      (this.nowTime - truncDay * this.oneDaySeconds) / this.oneHourSeconds // 得到小时的值
    let truncHour = Math.trunc(needHour) // 小时取整

    // 计算此时的分数
    // ================================================================
    // 得到分钟的值
    let needMinute =
      (this.nowTime -
        truncDay * this.oneDaySeconds -
        truncHour * this.oneHourSeconds) /
      this.oneMinuteSeconds
    // 分钟取整
    let truncMinute = Math.trunc(needMinute)

    // 计算此时的秒数
    // ================================================================
    let needSeconds =
      this.nowTime -
      truncDay * this.oneDaySeconds -
      truncHour * this.oneHourSeconds -
      truncMinute * this.oneMinuteSeconds

    // 时间内容承接对象 同步对应内容值
    // ================================================================
    this.timeObj.day = truncDay // 天数取整值
    this.timeObj.hour = truncHour // 小时取整值
    this.timeObj.minute = truncMinute // 分钟取整值
    this.timeObj.second = needSeconds // 秒钟取整值
    this.timeObj.time = this.nowTime // 返回当前的时间值

    // 使用传入的回调函数带上当前的时间内容
    this.userCallBack(this.timeObj)
  }
}

/**
 * @倒计时类
 * @information 传入的一个对象
 * callback:回调的方法(会赋予一个返回结果对象作为参数)
 * start:倒计时开始的时间(字符串 或者 时间戳)，默认为当前的时间戳
 * end:倒计时结束的时间(字符串 或者 时间戳)，默认为当前时间后一小时
 * independentData:额外独立计算数值(可选)
 * 注意 :
 *    1、start和end为字符串时，必须是可以被 new Date() 方法解析的格式，如果不是则会出现错误的情况
 *    2、independentData传入只能是总计的秒数且只能是数值
 *    2、independentData传入后，start和end参数会失效，只会以independentData为准进行计算
 * @返回结果为一个对象
 * day：天数；hour：小时；minute：分钟；second：秒钟
 *
 * @打点调用方法
 * open:开启循环倒计时函数
 * end:停止循环倒计时,返回此时停止的时间对象
 * again:重置倒计时开始和结束时间戳
 * 注意:
 *    1、again方法支持三个参数 start end independentData 其类型和作用与构造函数传参一致
 *    2、again若是要使用时间戳即start与end方式时，independentData参数则不用进行赋值，只需要传入start与end的参数属性即可
 *       示例：.again('2023-1-2 12:00:00','2023-1-3 12:00:00')
 *    3、again若是需要执行 independentData 的值，则传入第三个参数，且为数值类型即可，前面两个参数可以传空字符串
 *       示例：.again('','',500)
 */
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// 入参的接口
interface CountDown_information {
  callback?: Function
  start?: string | number
  end?: string | number
  independentData?: number
}

// 时间内容承接对象的接口
interface CountDown_timeObj {
  day: number
  hour: number
  minute: number
  second: number
}

// CountDown类本体
export class CountDown {
  protected timer: NodeJS.Timer // 定时器承接值
  protected timeObj: CountDown_timeObj // 时间内容承接对象
  protected startTime: number // 开始时间的时间戳
  protected endTime: number // 结束时间的时间戳
  protected additionalCalculation: number = -1 // 额外计算参数，默认值为 -1
  protected needTime: number = -1 // 当前倒计时的承接值，默认值为 -1
  protected oneDaySeconds: number // 一天的总秒数
  protected oneHourSeconds: number // 一小时的总秒数
  protected oneMinuteSeconds: number // 一分钟的总秒数
  protected userCallBack: Function // 定时器中的回调函数

  // 构造函数
  // ------------------------------------------------------------------------------
  constructor(information: CountDown_information = {}) {
    // 从传入的参数中进行解构并进行参数初始化
    const {
      callback = (time: CountDown_timeObj) => {}, // 赋予默认的执行方法
      start = new Date().getTime(), // 赋予默认的参数为当前的时间戳
      end = new Date().getTime() + 60 * 60 * 1000, // 赋予默认的参数为当前的时间戳的后一个小时
      independentData = -1, // 额外计算参数默认值为-1
    } = information

    // 执行规则函数得到当前值是否符合规则
    // ================================================================
    let rust = this.ruleValidation(
      start,
      end,
      callback,
      independentData,
      '构造'
    )

    // 判断当前的值如果不符合规则则直接弹出
    if (!rust) {
      return
    }

    // 时间内容承接对象进行初始化话
    // ================================================================
    this.timeObj = {
      day: 0,
      hour: 0,
      minute: 0,
      second: 0,
    }

    // 定时器中的回调函数初始化
    // ================================================================
    this.userCallBack = callback // 回调函数赋值
    this.oneDaySeconds = 60 * 60 * 24 // 一天的总秒数初始化
    this.oneHourSeconds = 60 * 60 // 一小时的总秒数初始化
    this.oneMinuteSeconds = 60 // 一分钟的总秒数初始化
    // 给需要的时间戳进行赋值
    independentData === -1
      ? (this.needTime = (this.endTime - this.startTime) / 1000)
      : (this.needTime = this.additionalCalculation)
  }

  /**
   * @开启倒计时函数
   * @定时器值赋予timer
   */
  // ------------------------------------------------------------------------------
  open: () => void | boolean = () => {
    // 防止多次open调用，故在每次open时清理一次定时器
    clearInterval(this.timer)

    // 首先在此执行规则验证方法，确定是否符合规则的
    let statue = this.ruleValidation(
      this.startTime,
      this.endTime,
      this.userCallBack,
      this.additionalCalculation,
      'open',
      false
    )

    // 如果不符合则弹出
    if (!statue) {
      console.error('参数类型错误，请检查传入的构造函数或again函数的传参！')

      return
    }

    // 给定时器参数赋值，并进行每秒循环定时器调用
    this.timer = setInterval(() => {
      this.needTime -= 1 // 所需时间自减1

      // 判断当倒计时完成时则清除定时器
      if (this.needTime < 0) {
        clearInterval(this.timer)
        return
      }

      // 调用数据中转函数，并给时间对象赋值
      this.timeObj = this.dataRelay()

      // 调用回调函数，并给回调函数赋予参数
      this.userCallBack(this.timeObj)
    }, 1000)
  }

  /**
   * @停止循环计时
   * @返回此时停止的时间对象
   */
  // ------------------------------------------------------------------------------
  end: () => CountDown_timeObj = () => {
    // 清理定时器
    clearInterval(this.timer)

    // 弹出当前的时间对象
    return this.timeObj
  }

  /**
   * @重置倒计时
   * @start 修改后的开始时间戳，类型同构造函数传入一致
   * @end 修改后的结束时间戳，类型同构造函数传入一致
   * @independentData 额外独立计算数值，同构造函数传入一致
   * @返回此时重置的时间对象或者是没有验证通过的布尔值
   */
  // ------------------------------------------------------------------------------
  again: (
    start: number | string,
    end: number | string,
    independentData?: number
  ) => CountDown_timeObj | boolean = (start, end, independentData = -1) => {
    // 清理定时器
    clearInterval(this.timer)
    // 给额外计算参数重新赋值
    this.additionalCalculation = independentData

    // 执行规则验证方法
    let againRust = this.ruleValidation(
      start,
      end,
      this.userCallBack,
      independentData,
      'again'
    )

    // 判断是否验证通过
    if (!againRust) {
      // 若没有则直接返回false
      return false
    }

    // 给需要的时间戳进行赋值
    independentData === -1
      ? (this.needTime = (this.endTime - this.startTime) / 1000)
      : (this.needTime = this.additionalCalculation)

    // 给timeObj进行重新赋值
    this.timeObj = this.dataRelay()

    // 弹出当前的时间对象
    return this.timeObj
  }

  /**
   * @规则验证方法
   * @start 开始的时间戳
   * @end 结束的时间戳
   * @callback 回调函数
   * @funName 执行函数命名（可选）
   * @showErr 是否显示错误提示（可选）
   * @返回一个布尔值 true验证通过 false验证失败
   */
  // ------------------------------------------------------------------------------
  protected ruleValidation: (
    start: number | string,
    end: number | string,
    callback: Function,
    data: number,
    funName?: string,
    showErr?: boolean
  ) => boolean = (start, end, callback, data, funName, showErr = true) => {
    // 首先判断传入的data类型是不是数值，并且data的不等于-1
    if (typeof data !== 'number' && data !== -1 && data) {
      showErr &&
        console.error(
          `independentData参数应该是数值number类型，${
            funName ? funName + '函数中' : ''
          }当前传入的类型为${typeof data},请注意修改!`
        )
      return false
    }

    // 判断是否需要开启额外计算属性
    if (data !== -1) {
      // 是的话，则进行对needTime属性赋值
      this.additionalCalculation = data
      return true
    }

    // 传入内容的类型判断+报错预提醒
    // ================================================================
    let startType = typeof start // 判断传入开始时间的类型
    let endType = typeof end // 判断传入结束时间的类型
    let callBackType = callback instanceof Function // 判断传入回调函数的类型

    // 首先判断是否传入的参数不符合类型情况
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    if (
      (startType !== 'string' && startType !== 'number') ||
      (endType !== 'string' && endType !== 'number') ||
      !callBackType
    ) {
      showErr &&
        console.error(
          `请输入正确的参数类型，${
            funName ? funName + '函数中' : ''
          }当前的传入的类型为start是${startType}; end是${endType} callback${
            callBackType ? '是function' : '不是function'
          };`
        )
      return false
    }

    // 给开始和结束时间戳进行赋值+判断这些值是否是可以使用的+开始与结束时间戳是否是大小为正确的
    // ================================================================
    this.startTime = new Date(start).getTime() // 赋值给开始时间戳
    this.endTime = new Date(end).getTime() // 赋值给结束时间戳

    // 判断这些值是否是NaN
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    if (!this.startTime || !this.endTime) {
      showErr &&
        console.error(
          `请输入正确的时间戳参数:${
            funName ? funName + '函数中' : ''
          }当前输入的start为 ${start} ; end为 ${end}`
        )
      return false
    }

    // 开始时间戳是否比结束时间戳大
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    if (this.startTime > this.endTime) {
      showErr &&
        console.error(
          `传入的开始时间戳不能大于结束时间戳，${
            funName ? funName + '函数中' : ''
          }当前传入的start为 ${start} ; end为 ${end}，请传入正确的值！`
        )
      return false
    }

    // 都满足则返回true
    return true
  }

  /**
   * @数据中转函数
   * @返回类型为CountDown_timeObj的一个对象
   */
  // ------------------------------------------------------------------------------
  protected dataRelay: () => CountDown_timeObj = () => {
    // 计算此时的天数
    // ================================================================
    let needDay = this.needTime / this.oneDaySeconds //得到天数的值
    let truncDay = Math.trunc(needDay) // 天数取整

    // 计算此时的小时
    // ================================================================
    let needHour =
      (this.needTime - truncDay * this.oneDaySeconds) / this.oneHourSeconds // 得到小时的值
    let truncHour = Math.trunc(needHour) // 小时取整

    // 计算此时的分数
    // ================================================================
    // 得到分钟的值
    let needMinute =
      (this.needTime -
        truncDay * this.oneDaySeconds -
        truncHour * this.oneHourSeconds) /
      this.oneMinuteSeconds
    // 分钟取整
    let truncMinute = Math.trunc(needMinute)

    // 计算此时的秒数
    // ================================================================
    let needSeconds =
      this.needTime -
      truncDay * this.oneDaySeconds -
      truncHour * this.oneHourSeconds -
      truncMinute * this.oneMinuteSeconds
    // 秒钟取整
    let truncSeconds = Math.trunc(needSeconds)

    return {
      day: truncDay,
      hour: truncHour,
      minute: truncMinute,
      second: truncSeconds,
    }
  }
}

/**
 * @文本信息复制到粘贴版方法
 * @message 需要复制到粘贴板的文本
 * @successCallback 成功的回调函数
 * @failCallback 失败的回调函数,会携带失败的信息，若是正常复制失败则是返回 复制失败
 */
// ------------------------------------------------------------------------------
interface TextCopy {
  (
    message: string,
    successCallback?: Function,
    failCallback?: (err?: string) => void
  ): void
}
export const textCopy: TextCopy = (
  message,
  successCallback = () => {},
  failCallback = () => {}
) => {
  // 判断若是不支持新式的复制文本方法
  // ================================================================
  if (!navigator.clipboard) {
    // 1.创建一个可选中元素
    let textArea: HTMLTextAreaElement = document.createElement('textarea')
    // 将当前需要同步的内容丢到创建的元素里面
    textArea.value = message
    // 2.使用定位，阻止页面滚动
    textArea.style.top = '0'
    textArea.style.left = '0'
    textArea.style.position = 'fixed'
    // 将创建的元素放到body里
    document.body.appendChild(textArea)
    // 文本获取焦点
    textArea.focus()
    // 选中所有的文本信息
    textArea.select()

    // 防错处理
    try {
      // 执行复制文本方法
      var successful: boolean = document.execCommand('copy')

      // 判断是否成功复制,成功则调用复制成功的方法，否则是复制失败的方法
      successful ? successCallback() : failCallback('复制失败')
    } catch (err) {
      // 复制失败的方法
      failCallback(err)
    }
    // 3.移除元素
    document.body.removeChild(textArea)
    return
  }

  // 支持的话则进行使用最新的文本复制方法
  // ================================================================
  navigator.clipboard.writeText(message).then(
    function () {
      // 执行复制成功的回调
      successCallback()
    },
    function (err) {
      // 复制失败的方法
      failCallback(err)
    }
  )
}

/**
 * @翻页筛选
 * @list 过滤筛选的数组
 * @page 页数，默认1
 * @pageSize 一页展示的条数，默认5
 * @返回一个对象
 * needList 过滤后需要的对应数组
 * totalNumber 总条数
 * totalPage 总页数
 */
// ------------------------------------------------------------------------------
interface FlipFilter {
  <T>(list: T[], page: number, pageSize: number): {
    needList: T[]
    totalNumber: number
    totalPage: number
  }
}

export const flipFilter: FlipFilter = (list, page = 1, pageSize = 5) => {
  // 计算所需的总长度
  let totalLength = page * pageSize

  // 计算起始位置
  let startIndex = totalLength - pageSize

  // 计算中条数
  let totalNumber = list.length

  // 计算总页数
  let totalPage = Math.ceil(totalNumber / pageSize)

  return {
    needList: list.slice(startIndex, totalLength),
    totalNumber,
    totalPage,
  }
}

/**
 * @前端base64编码加解密函数
 * @data 传入进行加解密的数据
 * @type 加密或解密 （1:加密，0:解密） 默认1
 */
// ------------------------------------------------------------------------------
interface Base64EncryptionDecryption {
  <T, U>(data: T, type?: number): U | string
}

export const base64EncryptionDecryption: Base64EncryptionDecryption = <T, U>(
  data: T,
  type = 1
) => {
  // 错误信息拦截判断
  if (!data) {
    console.error('传入的data数据不能为空')
    return
  }

  // 声明数据中转参数
  let transferParameters: U | string

  // 根据传入的type参数进行判断是加密还是解密
  switch (type) {
    case 1:
      // 加密时，先对data参数进行json字符串格式化，然后进行url编码转化，最后进行base64加密
      transferParameters = window.btoa(encodeURIComponent(JSON.stringify(data)))
      break

    case 0:
      // 解密时，先进行base64编码解密，然后进行url编码解密，然后进行Json还原
      transferParameters = JSON.parse(
        decodeURIComponent(window.atob(data as unknown as string))
      )
      break

    default:
      console.error(
        `type参数只能为数值1或0，请输入正确的type值，当前输入值为${type}`
      )
      break
  }

  // 弹出最后得到的中转参数
  return transferParameters
}

/**
 * @字符串替换函数
 * @sub 需要替换的字符串
 * @type 替换的类型 (start:从头开始替换,among:从中间开始替换,end:从末尾开始替换)；默认start
 * @fixedPosition 替换的长度,数值类型，即需要变成替换字符串*的个数，
 *                若是在 among 模式下，此属性为number[]类型，支持两个参数，第一个为头部保留原本字符的长度值，第二个成员为结束保留原本字符的长度值
 * @replace 替换后的字符串，默认 *
 */
// ------------------------------------------------------------------------------
interface CharacterSubstitution {
  (
    sub: string,
    type: string,
    fixedPosition: number | [number, number],
    replace?: string
  ): void | string
}

export const characterSubstitution: CharacterSubstitution = (
  sub = '',
  type = 'start',
  fixedPosition = 0,
  replace = '*'
) => {
  // 如果不是则进行报错提醒
  if (typeof sub !== 'string') {
    console.error(
      `传入的sub参数并非string类型，请检查传入值，当前类型为${typeof sub}`
    )
    return
  }

  // 将字符替换为对应的数组
  let str: string[] = sub.split('')

  // 感觉传入的位置进行分配截取方法
  switch (type) {
    case 'start':
      // 首先判断是否是数组，若不是则进行弹错
      if (typeof fixedPosition !== 'number') {
        console.error(
          `当前为开始截取模式，即type=start，此时的fixedPosition应该为Number形式，请检查传入的类型,当前传入的fixedPosition值为${fixedPosition}`
        )

        return
      }

      // 如果是起始位置替换，则进行循环判断当前位置是否小于截取的长度,若是成员为一个用值的空字符串不用替代
      str.forEach((value, index) => {
        if (index < fixedPosition && value !== ' ') {
          // 是的话则进行替换为 替换字符
          str[index] = replace
        }
      })
      break

    case 'among':
      // 首先判断是否是数组，若不是则进行弹错
      if (!Array.isArray(fixedPosition)) {
        console.error(
          `当前为中间截取模式，即type=among，此时的fixedPosition应该为Number[]形式，请检查传入的类型,当前传入的fixedPosition值为${fixedPosition}`
        )

        return
      }

      // 如果是起始位置替换，则进行循环判断当前位置是否小于截取的长度,若是成员为一个用值的空字符串不用替代
      str.forEach((value, index) => {
        if (
          index >= fixedPosition[0] &&
          index < str.length - fixedPosition[1] &&
          value !== ' '
        ) {
          // 是的话则进行替换为 替换字符
          str[index] = replace
        }
      })

      break

    case 'end':
      // 首先判断是否是数组，若不是则进行弹错
      if (typeof fixedPosition !== 'number') {
        console.error(
          `当前为结束截取模式，即type=end，此时的fixedPosition应该为Number形式，请检查传入的类型,当前传入的fixedPosition值为${fixedPosition}`
        )

        return
      }

      // 如果是结束位置替换，则进行循环判断当前位置是否大于此字符串的总长度-1-截取的长度,若是成员为一个用值的空字符串不用替代
      str.forEach((value, index) => {
        if (index > str.length - 1 - fixedPosition && value !== ' ') {
          // 是的话则进行替换为 替换字符
          str[index] = replace
        }
      })
      break

    default:
      console.error(
        `请输入正确的type值,目前type支持start、among、end，当前传入的type为${type}`
      )
      break
  }

  // 弹出处理好的字符串
  return str.join('')
}

/**
 * @内存处理类
 *
 * 全部的静态方法：
 * @getSession 访问回话存储成员 (传入查询的成员名称item；返回查询的成员值，若没有则是null)
 * @getBatchSession 批量访问回话存储成员 (传入查询的成员名称组成的string[]；返回查询的成员的键值对列表)
 * @setSession 设置回话存储成员 (传入修改的成员名称item、设置的值content)
 * @setBatchSession 批量设置回话存储成员 (传入修改的 成员名称name 和 设置值value 组成的对象列表)
 * @removeSession 删除回话存储成员 (传入删除的成员名称item)
 * @removeBatchSession 批量删除回话存储成员 (传入删除的成员名称组成的string[])
 * @getAllSession 访问全部回话存储 (返回目前回话存储中设置的所有成员集中对象)
 * @removeAllSession 删除全部回话存储
 *
 * @getLocal 访问长效存储成员 (传入查询的成员名称item；返回查询的成员值，若没有则是null)
 * @getBatchLocal 批量访问长效存储成员 (传入查询的成员名称组成的string[]；返回查询的成员的键值对列表)
 * @setLocal 设置长效存储成员 (传入修改的成员名称item、设置的值content)
 * @setBatchLocal 批量设置长效存储成员 (传入修改的 成员名称name 和 设置值value 组成的对象列表)
 * @removeLocal 删除长效存储成员 (传入删除的成员名称item)
 * @removeBatchLocal 批量删除长效存储成员 (传入删除的成员名称组成的string[])
 * @getAllLocal 访问全部长效存储 (返回目前长效存储中设置的所有成员集中对象)
 * @removeAllLocal 删除全部长效存储
 */
// -------------------------------------------------------------------------------------------------------------------------------
export class MemoryProcessing {
  // 回话存储处理
  // ==========================================================================================================================
  // 访问回话存储成员
  static getSession = (item: string) =>
    window.sessionStorage.getItem(item)
      ? JSON.parse(window.sessionStorage.getItem(item))
      : null

  // 批量访问回话存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static getBatchSession = (itemList: string[]) => {
    const needList = itemList.map((value) => {
      const itemValue = {}
      itemValue[value] = this.getSession(value)
      return itemValue
    })

    return needList
  }

  // 设置回话存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static setSession: <T>(item: string, content: T) => void = (item, content) =>
    window.sessionStorage.setItem(item, JSON.stringify(content))

  // 批量设置回话存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static setBatchSession = (
    itemList: Array<{ name: string; value: string }>
  ) => {
    itemList.forEach((value) => this.setSession(value.name, value.value))
  }

  // 删除回话存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static removeSession = (item: string) =>
    window.sessionStorage.removeItem(item)

  // 批量删除回话存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static removeBatchSession = (itemList: string[]) => {
    itemList.forEach((value) => this.removeSession(value))
  }

  // 访问全部回话存储
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static getAllSession = () => {
    // 内存数据承接对象
    const session = {}

    // 循环所有的回话存储的成员
    for (let index = 0; index < window.sessionStorage.length; index++) {
      // 获取到当前的内容key值
      const key = window.sessionStorage.key(index)

      // 给承接对象赋予解析后的真实值
      session[key] = this.getSession(key)
    }

    // 弹出处理好后的对象
    return session
  }

  // 删除全部回话存储
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static removeAllSession = () => window.sessionStorage.clear()

  // 长效存储处理
  // ==========================================================================================================================
  // 访问长效存储成员
  static getLocal = (item: string) =>
    window.localStorage.getItem(item)
      ? JSON.parse(window.localStorage.getItem(item))
      : null

  // 批量访问长效存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static getBatchLocal = (itemList: string[]) => {
    const needList = itemList.map((value) => {
      const itemValue = {}
      itemValue[value] = this.getLocal(value)
      return itemValue
    })

    return needList
  }

  // 设置长效存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static setLocal: <T>(item: string, content: T) => void = (item, content) =>
    window.localStorage.setItem(item, JSON.stringify(content))

  // 批量设置长效存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static setBatchLocal = (itemList: Array<{ name: string; value: string }>) => {
    itemList.forEach((value) => this.setLocal(value.name, value.value))
  }

  // 删除长效存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static removeLocal = (item: string) => window.localStorage.removeItem(item)

  // 批量删除长效存储成员
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static removeBatchLocal = (itemList: string[]) => {
    itemList.forEach((value) => this.removeLocal(value))
  }

  // 访问全部长效存储
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static getAllLocal = () => {
    // 内存数据承接对象
    const local = {}

    // 循环所有的回话存储的成员
    for (let index = 0; index < window.localStorage.length; index++) {
      // 获取到当前的内容key值
      const key = window.localStorage.key(index)

      // 给承接对象赋予解析后的真实值
      local[key] = this.getLocal(key)
    }

    // 弹出处理好后的对象
    return local
  }

  // 删除全部长效存储
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  static removeAllLocal = () => window.localStorage.clear()
}

console.log(dateFormat())
